﻿/// <reference name="MicrosoftAjax.js"/>
/// <reference path="jquery-1.5.1-vsdoc.js" />

var SCS = {};

SCS.MenuBehavior = function (element, settings) {

    if (typeof (element) == "undefined")
        throw new Error("Element passed is not valid.");

    if (typeof (element.id) == "undefined")
        throw new Error("Element passed must have an Id attribute set.");

    this.element = element;
    this.items = [];
    this.cssClasses = {};

    this._onKeyDelegate = null;

    this.hideDelay = (typeof (settings.hideDelay) != "undefined") ? settings.hideDelay : 1.5;
    this.menuClass = (typeof (settings.menuClass) != "undefined") ? settings.menuClass : "menu-h";
    this.itemClass = (typeof (settings.itemClass) != "undefined") ? settings.itemClass : "item";
    this.menuType = (typeof (settings.menuType) != "undefined") ? settings.menuType : "H";
}

SCS.MenuBehavior.prototype = {
    initialize: function () {

        this.cssClasses.item = this.itemClass;
        this.cssClasses.itemHover = this.itemClass + "-hover";
        this.cssClasses.topItem = this.itemClass + "-top";
        this.cssClasses.topItemHover = this.itemClass + "-top-hover";
        this.cssClasses.itemDisabled = this.itemClass + "-disabled";

        var elem = $(this.element),
            rootItems = elem.children();

        elem.wrap("<span class='" + this.menuClass + "'></span>");

        var baseId = this.element.id,
            level = -1;

        for (var i = 0; i < rootItems.length; i++)
            this._buildItems(rootItems[i], this.items, null, baseId + "_" + i, level);

        this.menuType = (rootItems.css("float") != "left") ? "V" : "H";

        this._onKeyDelegate = $.proxy(this._onKeyDown, this);
        $(document).bind("keydown", this._onKeyDelegate);
    },

    dispose: function () {

        for (var i = 0; i < this.items.length; i++)
            this._disposeItems(this.items[i]);

        $(document).unbind("keydown", this._onKeyDelegate);
    },

    find: function (text) {

        var anchor = $("li > a:contains('" + text + "')", this.element);

        if (anchor)
            return anchor.parent("li").attr("mi");

        return null;
    },

    _disposeItems: function (item) {

        if (item.items)
            for (var i = 0; i < item.items.length; i++)
                this._disposeItems(item.items[i]);

        item.dispose();
    },

    _buildItems: function (elem, items, parentItem, id, level) {

        level++;

        var newItem = new SCS.MenuItem
        {
            newItem.id = id;
            newItem.element = elem;
            newItem.parent = (parentItem) ? parentItem : null;
            newItem.root = this;
            newItem.level = level;
        };

        newItem.initialize();

        elem.className = (level == 0) ? this.cssClasses.topItem : this.cssClasses.item;
        elem.mi = newItem;

        if (parentItem)
            parentItem.items[parentItem.items.length] = newItem;
        else
            items[items.length] = newItem;

        var childItems = $("ul", elem).eq(0).children();

        if (childItems.length > 0)
            $(elem).children("a").append("<span/>");

        for (var i = 0; i < childItems.length; i++)
            this._buildItems(childItems[i], items, newItem, id + "_" + i, level);
    },

    _onKeyDown: function (e) {

        var item = $(e.target).parent("li").attr("mi");

        if (item) {

            e.preventDefault();

            var elem = $(item.element),
                next = null,
                menu = null,
                keyCode = e.keyCode;

            //Sys.Debug.fail("");

            // special case for top level items on horizontal menu.
            if (this.menuType.substring(0, 1) === "H" && item.level === 0) {

                switch (keyCode) {

                    case 37: // left key 
                        keyCode = 38; // change to up key
                        break;
                    case 39: // right key
                        keyCode = 40; // change to down key
                        break;
                    case 38: // up key 
                        keyCode = 37; // change to left key
                        break;
                    case 40: // down key
                        keyCode = 39; // change to right key
                        break;
                }
            }

            switch (keyCode) {

                // tab key                                                                                 
                case 9:

                    if (e.shiftKey) {

                        next = elem.prev("li");

                        if (next.length < 1) {

                            next = elem.parents("li").eq(0);

                            if (next.length > 0) {
                                // go to owner node in parent menu and close child menu
                                next.attr("mi").hideMenu();
                            }

                            if (next.length < 1) {
                                // go to last item in current menu 
                                next = elem.parent("ul").children("li").last();
                            }
                        }
                    }
                    else {

                        menu = elem.children("ul");

                        if (menu.length > 0) {

                            // go to first item in child menu
                            next = menu.children("li").first();

                            // ensure other open menus are closed first
                            this.hideMenus();

                            // show child menu
                            next.attr("mi").showMenu();
                        }
                        else {
                            next = elem.next();

                            var hideChild = null;

                            if (next.length < 1) {
                                // go to next item in parent menu and close child menu
                                next = elem.parents("li").eq(0).next("li");
                                hideChild = 1;
                            }

                            if (next.length < 1) {
                                // go to first item in parent menu and close child menu
                                next = elem.parents("ul").eq(1).children("li").first();
                                hideChild = 1;
                            }

                            if (hideChild)
                                elem.parents("li").attr("mi").hideMenu();
                        }
                    }
                    break;

                // escape key                                                                                 
                case 27:

                    // left arrow                       
                case 37:

                    menu = elem.parents("ul").eq(1);

                    if (menu.length > 0) {
                        // hide the child menu we're leaving
                        next = elem.parents("li").eq(0);
                        next.attr("mi").hideMenu();
                    }
                    break;

                // right arrow                                                                                  
                case 39:

                    menu = elem.children("ul");

                    if (menu.length > 0) {
                        // current node contains a menu so show it
                        item.showMenu();
                        next = menu.children("li").first();
                    }
                    break;

                // up arrow	                                                                                 
                case 38:

                    next = elem.prev();

                    if (next.length < 1) {
                        // move to next item in current menu
                        next = elem.parent("ul").children("li").last();
                    }

                    break;

                // down arrow                                                                                 
                case 40:

                    next = elem.next();

                    if (next.length == 0) {
                        // move to previous item in current menu
                        next = elem.parent("ul").children("li").first();
                    }
                    break;
            }

            if (next && next.length > 0) {

                // go to the next selected item
                var nextItem = next.attr("mi");
                nextItem.focus();
                nextItem.highlight(true);

                // ensure mouse-over effect is removed from previous item
                item.highlight(false);
            }
        }
    },

    hideMenus: function () {

        this._hideMenusRecursive(this.items);
    },

    _hideMenusRecursive: function (items) {

        for (var i = 0; i < items.length; i++) {

            items[i].hideMenu();

            var childItems = items[i].items;

            if (childItems.length > 0)
                this._hideMenusRecursive(childItems);
        }
    }
}

SCS.MenuItem = function () {

    this.id = null;
    this.element = null;
    this.parent = null;
    this.root = null;
    this.items = [];
    this.level = null;

    this._a = null;
    this._ul = null;
    this._el = null;

    this._timout = null;

    this._showDelegate = null;
    this._hideDelegate = null;
    this._clickDelegate = null;
}

SCS.MenuItem.prototype = {
    initialize: function () {

        this._el = $(this.element);
        this._a = $(this._el).children("a");
        this._ul = $(this._el).children("ul");

        this._showDelegate = $.proxy(this._onMouseover, this);
        this._el.bind("mouseover.mi", this._showDelegate);

        this._hideDelegate = $.proxy(this._onMouseout, this);
        this._el.bind("mouseout.mi", this._hideDelegate);

        this._clickDelegate = $.proxy(this._onClick, this);
        this._el.bind("click.mi", this._clickDelegate);
    },
    dispose: function () {

        this._el.unbind("mi");
        this._showDelegate = this._hideDelegate = this._clickDelegate = null;

        this.element.mi = this.element = null;
    },

    _onMouseover: function (e) {

        this.highlight(true);
        this.showMenu();

        e.stopPropagation();
    },

    _onMouseout: function (e) {

        this.highlight(false);

        if (this.items.length == 0)
            return;

        var delay = this.root.hideDelay;

        if (!delay) {
            this.hideMenu();
        }
        else {
            this._timout = window.setTimeout($.proxy(this.hideMenu, this), delay * 1000);
        }
    },

    _onClick: function (e) {

        this.root.hideMenus();
    },

    showMenu: function () {

        // close any open menus
        this.root.hideMenus(this);

        if (this.getEnabled()) {

            // to show this menu we need to ensure
            // all parent menus are shown as well
            var menu = this;

            while (menu = menu.parent)
                menu._showMenu();

            // show this menu
            if (this.items.length > 0)
                this._showMenu();
        }
    },

    _showMenu: function () {
        this._cancelHide();
        this._ul.show();
    },

    _cancelHide: function () {

        if (this._timout)
            window.clearTimeout(this._timout);

        this._timout = null;
    },

    hideMenu: function () {
        this._ul.hide();
    },

    focus: function () {
        this._a.get(0).focus();
    },

    highlight: function (state) {

        if (this.getEnabled()) {

            var className = (this.level == 0) ? this.root.cssClasses.topItemHover : this.root.cssClasses.itemHover;

            if (state)
                this._el.addClass(className);
            else
                this._el.removeClass(className);
        }
    },

    getText: function () {
        return this._a.text();
    },

    setText: function (value) {
        this._a.text(value);
    },

    getEnabled: function () {
        return !this._a.attr("disabled");
    },

    setEnabled: function (value) {
        this._a.attr("disabled", !value);
    }
}